﻿#ifndef QLIB_HEADER_VEC2
#define QLIB_HEADER_VEC2

#include <inttypes.h>
#include <cmath>
#include <vector>

#include "qlib/core/export.h"

namespace qlib {

template <typename T>
class Vec2 {
 public:
  Vec2(T _x = 0, T _y = 0) : x(_x), y(_y) {};

  // 数据
  union {
    struct {
      T x, y;
    };
    struct {
      T u, v;
    };
    struct {
      T re, im;
    };
    struct {
      T e[2];
    };
  };

  double inner(const Vec2& vec) const { return e[0] * vec.e[0] + e[1] * vec.e[1]; }
  double length_sq() const { return inner(*this); }
  double length() const { return (double)sqrt(length_sq()); }
  double distance(const Vec2& vec) const {
    double dx = x - vec.x;
    double dy = y - vec.y;
    return std::sqrt(std::pow(dx, 2) + std::pow(dy, 2));
  }
  double distance2(const Vec2& vec) const {
    double dx = x - vec.x;
    double dy = y - vec.y;
    return std::pow(dx, 2) + std::pow(dy, 2);
  }
  double normalize() {
    double len = length();
    if (len > 0) {
      e[0] /= len;
      e[1] /= len;
    }
    return len;
  }

  double cross(const Vec2& vec) const { return x * vec.y - y * vec.x; }
  double angle() const { return atan2(y, x); }
  double angle_vec(const Vec2& vec) const { return atan2(cross(vec), inner(vec)); }

  double dot(const Vec2& vec) const { return x * vec.x + y * vec.y; }

  Vec2 ortho() const { return Vec2{-e[1], e[0]}; }

  void mirror(const Vec2& p0, const Vec2& p1) {
    Vec2 v = p1 - p0;
    double tmp = v.length_sq();
    if (tmp == 0)
      return;
    Vec2 r = v * (2 / tmp);
    Vec2 p2 = p0 * 2;
    *this = v * (*this - p0).inner(r) - *this + p2;
  }

  // 比较运算
  bool operator==(const Vec2& vec) const { return e[0] == vec.e[0] && e[1] == vec.e[1]; }
  bool operator!=(const Vec2& vec) const { return e[0] != vec.e[0] || e[1] != vec.e[1]; }

  bool operator<(const Vec2& vec) const { return e[0] < vec.e[0] || (e[0] == vec.e[0] && (e[1] < vec.e[1])); }
  bool operator>(const Vec2& vec) const { return e[0] > vec.e[0] || (e[0] == vec.e[0] && (e[1] > vec.e[1])); }
  bool operator<=(const Vec2& vec) const { return e[0] <= vec.e[0] || (e[0] == vec.e[0] && (e[1] <= vec.e[1])); }
  bool operator>=(const Vec2& vec) const { return e[0] >= vec.e[0] || (e[0] == vec.e[0] && (e[1] >= vec.e[1])); }

  // 向量运算
  Vec2 operator+(const Vec2& vec) const {
    Vec2 res = (*this);
    res.x += vec.x;
    res.y += vec.y;
    return res;
  }
  Vec2 operator+(T value) const {
    Vec2 res = (*this);
    res.x += value;
    res.y += value;
    return res;
  }

  Vec2 operator-(const Vec2& vec) const {
    Vec2 res = (*this);
    res.x -= vec.x;
    res.y -= vec.y;
    return res;
  }
  Vec2 operator-(T value) const {
    Vec2 res = (*this);
    res.x -= value;
    res.y -= value;
    return res;
  }

  Vec2 operator*(const Vec2& vec) const {
    Vec2 res = (*this);
    res.x *= vec.x;
    res.y *= vec.y;
    return res;
  }
  Vec2 operator*(T value) const {
    Vec2 res = (*this);
    res.x *= value;
    res.y *= value;
    return res;
  }

  Vec2 operator/(const Vec2& vec) const {
    Vec2 res = (*this);
    res.x /= vec.x;
    res.y /= vec.y;
    return res;
  }
  Vec2 operator/(T value) const {
    Vec2 res = (*this);
    res.x /= value;
    res.y /= value;
    return res;
  }

  // 赋值运算
  Vec2& operator+=(const Vec2& vec) {
    e[0] += vec.e[0];
    e[1] += vec.e[1];
    return *this;
  }
  Vec2& operator+=(const double s) {
    e[0] += s;
    e[1] += s;
    return *this;
  }

  Vec2& operator-=(const Vec2& vec) {
    e[0] -= vec.e[0];
    e[1] -= vec.e[1];
    return *this;
  }
  Vec2& operator-=(const double s) {
    e[0] -= s;
    e[1] -= s;
    return *this;
  }

  Vec2& operator*=(const Vec2& vec) {
    e[0] *= vec.e[0];
    e[1] *= vec.e[1];
    return *this;
  }
  Vec2& operator*=(const double s) {
    e[0] *= s;
    e[1] *= s;
    return *this;
  }

  Vec2& operator/=(const Vec2& vec) {
    e[0] /= vec.e[0];
    e[1] /= vec.e[1];
    return *this;
  }
  Vec2& operator/=(const double s) {
    e[0] /= s;
    e[1] /= s;
    return *this;
  }
};

template <typename T>
inline Vec2<T> operator-(const Vec2<T>& vec) {
  return Vec2<T>{-vec.e[0], -vec.e[1]};
}

template <typename T>
inline Vec2<T> operator+(const Vec2<T>& v1, const Vec2<T>& v2) {
  return Vec2<T>{v1.e[0] + v2.e[0], v1.e[1] + v2.e[1]};
}

template <typename T>
inline Vec2<T> operator+(const Vec2<T>& vec, const double s) {
  return Vec2<T>{vec.e[0] + s, vec.e[1] + s};
}

template <typename T>
inline Vec2<T> operator+(const double s, const Vec2<T>& vec) {
  return Vec2<T>{s + vec.e[0], s + vec.e[1]};
}

template <typename T>
inline Vec2<T> operator-(const Vec2<T>& v1, const Vec2<T>& v2) {
  return Vec2<T>{v1.e[0] - v2.e[0], v1.e[1] - v2.e[1]};
}

template <typename T>
inline Vec2<T> operator-(const Vec2<T>& vec, const double s) {
  return Vec2<T>{vec.e[0] - s, vec.e[1] - s};
}

template <typename T>
inline Vec2<T> operator-(const double s, const Vec2<T>& vec) {
  return Vec2<T>{s - vec.e[0], s - vec.e[1]};
}

template <typename T>
inline Vec2<T> operator*(const Vec2<T>& v1, const Vec2<T>& v2) {
  return Vec2<T>{v1.e[0] * v2.e[0], v1.e[1] * v2.e[1]};
}

template <typename T>
inline Vec2<T> operator*(const Vec2<T>& vec, const double s) {
  return Vec2<T>{vec.e[0] * s, vec.e[1] * s};
}

template <typename T>
inline Vec2<T> operator*(const double s, const Vec2<T>& vec) {
  return Vec2<T>{s * vec.e[0], s * vec.e[1]};
}

template <typename T>
inline Vec2<T> operator/(const Vec2<T>& v1, const Vec2<T>& v2) {
  return Vec2<T>{v1.e[0] / v2.e[0], v1.e[1] / v2.e[1]};
}

template <typename T>
inline Vec2<T> operator/(const Vec2<T>& vec, const double s) {
  return Vec2<T>{vec.e[0] / s, vec.e[1] / s};
}

template <typename T>
inline Vec2<T> operator/(const double s, const Vec2<T>& vec) {
  return Vec2<T>{s / vec.e[0], s / vec.e[1]};
}

template <typename T>
inline Vec2<T> cplx_conj(const Vec2<T>& z) {
  return Vec2<T>{z.re, -z.im};
};

template <typename T>
inline Vec2<T> cplx_mul(const Vec2<T>& z1, const Vec2<T>& z2) {
  return Vec2<T>{z1.re * z2.re - z1.im * z2.im, z1.re * z2.im + z1.im * z2.re};
};

template <typename T>
inline Vec2<T> cplx_inv(const Vec2<T>& z) {
  return cplx_conj(z) / z.length_sq();
};

template <typename T>
inline Vec2<T> cplx_div(const Vec2<T>& z1, const Vec2<T>& z2) {
  return cplx_mul(z1, cplx_conj(z2)) / z2.length_sq();
};

template <typename T>
inline Vec2<T> cplx_from_angle(T angle) {
  return Vec2<T>{cos(angle), sin(angle)};
};

using Vec2d = Vec2<double>;
using Vec2i = Vec2<int64_t>;

inline Vec2i cplx_round(const Vec2d& v) {
  return Vec2i(std::round(v.x), std::round(v.y));
};

// 计算两点角度
inline Vec2i cplx_angle(const Vec2d& v1, const Vec2d& v2) {
  return atan2(v2.y - v1.y, v2.x - v1.x);
};

// 计算中点
template <typename T>
inline T cacl_center_point(const Vec2<T>& p1, const Vec2<T>& p2) {
  return (p1 + p2) / 2;
}

inline void box_and_point(std::vector<Vec2d>& box, const Vec2d& point) {
  box.resize(2);

  auto& min = box[0];
  auto& max = box[1];

  if (point.x < min.x)
    min.x = point.x;
  if (point.x > max.x)
    max.x = point.x;

  if (point.y < min.y)
    min.y = point.y;
  if (point.y > max.y)
    max.y = point.y;
}

inline bool point_in_box(std::vector<Vec2d>& box, const Vec2d& point) {
  auto& min = box[0];
  auto& max = box[1];

  if (point.x < min.x)
    return false;
  if (point.x > max.x)
    return false;

  if (point.y < min.y)
    return false;
  if (point.y > max.y)
    return false;

  return true;
}

inline bool point_in_box(std::vector<Vec2d>& box, const Vec2d& point, double tolerance) {
  auto& min = box[0];
  auto& max = box[1];

  if (point.x < min.x && std::abs(point.x - min.x) > tolerance)
    return false;
  if (point.x > max.x && std::abs(point.x - max.x) > tolerance)
    return false;

  if (point.y < min.y && std::abs(point.y - min.y) > tolerance)
    return false;
  if (point.y > max.y && std::abs(point.y - max.y) > tolerance)
    return false;

  return true;
}

}  // namespace qlib

#endif  // QLIB_HEADER_VEC2
